package com.xyh.flowershop.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xyh.flowershop.cache.RedisCache;
import com.xyh.flowershop.constant.CacheConstant;
import com.xyh.flowershop.constant.CommonConstant;
import com.xyh.flowershop.context.WebContext;
import com.xyh.flowershop.domain.*;
import com.xyh.flowershop.enums.OrderStatusEnum;
import com.xyh.flowershop.mapper.*;
import com.xyh.flowershop.service.OrderService;
import com.xyh.flowershop.utils.CommonUtils;
import com.xyh.flowershop.utils.IdGenerator;
import com.xyh.flowershop.utils.OrderNoUtils;
import com.xyh.flowershop.vo.req.orders.OrderAddItem;
import com.xyh.flowershop.vo.req.orders.OrderAddReqVo;
import com.xyh.flowershop.vo.req.orders.OrderPageReqVo;
import com.xyh.flowershop.vo.resp.order.OrderDetailRespVo;
import com.xyh.flowershop.vo.resp.order.OrderItemRespVo;
import com.xyh.flowershop.vo.resp.order.OrderPageRespVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 小约翰可汗
 * @className OrderServiceImpl
 * @createTime 2024/5/1221:37
 */
@Slf4j
@Service
@Transactional(rollbackFor = RuntimeException.class)
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    private final GoodsMapper goodsMapper;

    private final OrderItemMapper OrderItemMapper;

    private final RedisCache redisCache;

    private final CartMapper cartMapper;

    private final TranMapper tranMapper;

    public OrderServiceImpl(GoodsMapper goodsMapper, OrderItemMapper OrderItemMapper, RedisCache redisCache, CartMapper cartMapper, TranMapper tranMapper) {
        this.goodsMapper = goodsMapper;
        this.OrderItemMapper = OrderItemMapper;
        this.redisCache = redisCache;
        this.cartMapper = cartMapper;
        this.tranMapper = tranMapper;
    }

    /**
     * 采用悲观锁进行订单的添加，防止超卖
     * @param vo
     * @return
     */
    @Override
    public  Boolean addOrder(OrderAddReqVo vo) {
        List<OrderAddItem> orderItems ;
        synchronized (this) {
            // 校验库存
             orderItems = vo.getOrderItems();
            List<String> goodIds = orderItems.stream().map(OrderAddItem::getGoodId).toList();
            List<Goods> goodsList = goodsMapper.selectByIds(goodIds);
            goodsList.forEach(goods -> {
                OrderAddItem orderItem = orderItems.stream().filter(i -> i.getGoodId().equals(goods.getId())).findFirst().get();
                if (goods.getTotalNum() < orderItem.getCount()) {
                    throw new RuntimeException(CommonConstant.FAIL);
                }
            });
            // 扣除库存
            goodsList.forEach(goods -> {
                goods.setTotalNum(goods.getTotalNum() - orderItems.stream().filter(e -> e.getGoodId().equals(goods.getId())).findFirst().get().getCount());
                goodsMapper.updateById(goods);
            });
        }
        // 订单入库
        Order order = new Order();
        BeanUtils.copyProperties(vo, order);
        order.setUserId(WebContext.get());
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());
        if(order.getOrderStatus().equals(OrderStatusEnum.UNPAID.getCode())){
            order.setExpireTime(LocalDateTime.now().plusMinutes(30));
            // TODO 将这个写入消息队列中，用于订单超时取消
        }
        order.setOrderNo(OrderNoUtils.generateOrderNo());
        baseMapper.insert(order);
        // 写入订单项
        List<OrderItem> orderItemList = orderItems.stream().map(e -> {
            OrderItem orderItem = new OrderItem();
            BeanUtils.copyProperties(e, orderItem);
            orderItem.setOrderId(order.getId());
//            orderItem.setId(IdGenerator.nextId().toString());
            return orderItem;
        }).toList();
        OrderItemMapper.batchInsert(orderItemList);
        if(orderItems.stream().noneMatch(e->Objects.isNull(e.getCartId()))){
            // 清理购物车订单缓存
            redisCache.deleteObject(CacheConstant.CART_ORDER_PREFIX + WebContext.get());
            List<String> cartsId = orderItems.stream().map(e -> e.getCartId().toString()).toList();
            cartMapper.removeCarts(cartsId);
        }


        if(order.getOrderStatus() == 1){
            // 调用交易接口
            Tran tran = new Tran();
            tran.setTranNo(order.getOrderNo());
            tran.setUserId(WebContext.get());
            tran.setTranTime(new Date());
            tran.setTranAmount(vo.getTotalPrice());
            tranMapper.insert(tran);
            //如果接入支付接口，那么此处需要去更改订单状态
        }
        return true;
    }

    @Override
    public Boolean deleteOrders(List<String> ids) {
        return baseMapper.removeOrders(ids) > 0;
    }

    @Override
    public void cancelOrders(List<String> ids) {
        // 订单状态改为取消
        baseMapper.cancelOrders(ids);
        List<Order> orders = baseMapper.selectByIds(ids);
        List<String> orderIds = orders.stream().map(Order::getId).toList();
        baseMapper.cancelOrders(orderIds);
        // 库存回滚
        List<OrderItem> orderItems = OrderItemMapper.selectByOrderIds(orderIds);
        List<Goods> goodsList = orderItems.stream().map(item ->{
            Goods goods = new Goods();
            goods.setId(item.getGoodId());
            goods.setTotalNum(item.getCount());
            return goods;
        }).toList();
        goodsMapper.rollbackStock(goodsList);
    }


    @Override
    public IPage<OrderPageRespVo> findPageOrders(OrderPageReqVo vo) {
        IPage<OrderPageRespVo> page = new Page<>(vo.getPageIndex(), vo.getPageSize());
        vo.setUserId(WebContext.get());
        page = baseMapper.findPageOrders(page, vo);
        List<OrderPageRespVo> orderPageRespVos = page.getRecords();
        orderPageRespVos.forEach(e -> {
//            e.setOrderDate(CommonUtils.formatDate(e.getOrderDate()));
            e.setStatusDesc(OrderStatusEnum.fromCode(e.getOrderStatus()).getDesc());
            List<OrderItemRespVo> orderItems = OrderItemMapper.selectOrderItemByOrderId(e.getId());
            e.setOrderItemRespVoList(orderItems);
        });
        page.setRecords(orderPageRespVos);
        return page;
    }

    @Override
    public OrderDetailRespVo findOrderDetail(String id) {
        Order order = baseMapper.selectById(id);
        if (Objects.isNull(order)) {
            return null;
        }
        OrderDetailRespVo respVo = new OrderDetailRespVo();
        BeanUtils.copyProperties(order, respVo);
        List<OrderItemRespVo> orderItems = OrderItemMapper.selectOrderItemByOrderId(id);
        Integer totalCount = orderItems.stream().mapToInt(OrderItemRespVo::getCount).sum();
        respVo.setTotalNum(totalCount);
        respVo.setCreateTime(CommonUtils.formatDate(order.getCreateTime()));
        respVo.setOrderItems(orderItems);
        return respVo;
    }

    @Override
    public Boolean confirmReceipt(String id) {
        return baseMapper.updateOrderStatus(id,OrderStatusEnum.RECEIVED.getCode()) > 0;
    }

    @Override
    public Boolean buyAgain(String id) {
        Order order = baseMapper.selectById(id);
        if(Objects.isNull(order)){
            return  false;
        }
        List<OrderItem> orderItemList = OrderItemMapper.selectByOrderIds(List.of(id));
        List<Cart> insertCartList = orderItemList.stream().map(item -> {
            Cart cart = new Cart();
            cart.setGoodId(item.getGoodId());
            cart.setCount(item.getCount());
            cart.setCount(item.getCount());
            cart.setTotalPrice(item.getTotalPrice());
            cart.setUserId(WebContext.get());
            return cart;
        }).toList();
        LambdaQueryWrapper<Cart> cartLambdaQueryWrapper = new LambdaQueryWrapper<>();
        cartLambdaQueryWrapper.eq(Cart::getUserId, WebContext.get());
        cartLambdaQueryWrapper.eq(Cart::getDeleted,false);
        List<Cart> cartList = cartMapper.selectList(cartLambdaQueryWrapper);
        // 过滤出需要修改的购物车数据
        /*List<Cart> editCartList = insertCartList.stream().filter(insertCart -> cartList.stream().anyMatch(
                cart -> insertCart.getGoodId().equals(cart.getGoodId())
        )).collect(Collectors.toList());*/
        // 过滤出需要新增的购物车数据,由于Arrays.asList()返回的List是固定不可变的，所以需要使用ArrayList
        List<Cart> newInsertCartList = new ArrayList<>(insertCartList);
        newInsertCartList.removeIf(insertCart -> cartList.stream().
                anyMatch(cart -> insertCart.getGoodId().equals(cart.getGoodId())));
        List<Cart> editCartList = new ArrayList<>();
        insertCartList.forEach(insertCart -> {
            // 找到原有购物车数据
            Optional<Cart> first = cartList.stream().filter(cart -> insertCart.getGoodId().equals(cart.getGoodId())).findFirst();
            if(first.isPresent()){
                editCartList.add(first.get());
            }
        });

         int row = 0;
         if(editCartList.size() > 0){
             editCartList.forEach(editCart -> {
                 editCart.setCount(editCart.getCount() + insertCartList.
                         stream().
                         filter(insertCart -> insertCart.getGoodId()
                                 .equals(editCart.getGoodId())).findFirst().get().getCount());
                 editCart.setTotalPrice(editCart.getTotalPrice()
                         .add( insertCartList.stream().
                                 filter(insertCart -> insertCart.getGoodId()
                                         .equals(editCart.getGoodId())).findFirst().get().getTotalPrice()));
             });
             row = cartMapper.batchUpdateCarts(editCartList);
         }
         if(newInsertCartList.size() > 0){
             row = cartMapper.batchInsertCarts(newInsertCartList);
         }
        log.info("需要新增的购物车：{}",insertCartList);
        log.info("需要修改的购物车：{}",editCartList);

        return row > 0;
    }
}
